#include "mainwindow.h"
#include <QApplication>
#include <iostream>
#include <functional>

using namespace std;

template <typename T>
void foo(T &t)
{
    t();
}

template <typename T>
void foo(const T *)
{
    cout << "type: const T*" << endl;
}

template <typename T>
void foo(T *)
{
    cout << "type: T*" << endl;
}

class function
{
public:
    function() : m_nCount(0) {}
    void operator()() {
        m_nCount ++;
        cout << "Count:" << m_nCount << endl;
    }
private:
    int m_nCount;
};

void print()
{
    cout << "Print" << endl;
}

////////////////////////////////////////////////////
template <class Predicate>
class my_unary_negate
        : public std::unary_function<typename Predicate::argument_type, bool>
{
public:
    explicit my_unary_negate(const Predicate &) { }
    bool operator() (const typename Predicate::argument_type &x) const {
        Predicate pred;
        return !pred(x);
    }
};

template<class Predicate>
inline my_unary_negate<Predicate> my_not1(const Predicate &pred)
{
    return my_unary_negate<Predicate>(pred);
}

template <class Predicate>
class my_unary_negate_ref
        : public std::unary_function<typename Predicate::argument_type, bool>
{
public:
    explicit my_unary_negate_ref(Predicate &pred) : m_pred(pred) { }
    bool operator() (const typename Predicate::argument_type &x) const {
        return !m_pred(x);
    }

private:
    Predicate &m_pred;
};

template<class Predicate>
inline my_unary_negate_ref<Predicate> my_not_ref(Predicate &pred)
{
    return my_unary_negate_ref<Predicate>(pred);
}

////////////////////////////////////////////////////
bool yes(bool r) {return r;}

class IsOdd : public unary_function<int, bool>
{
public:
    IsOdd() : m_nCount(0) {}

    bool operator()(int x) {
        cout << "IsOdd: " << ++ m_nCount << endl;
        return ((x % 2) == 0);
    }
private:
    int m_nCount;
};

////////////////////////////////////////////////////

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    function f;
    foo(f);
    foo(f);
    foo(print);
    foo(f);
    foo(&f);
    const int a1 = 0;
    foo(&a1);
    int b;
    foo(&b);

    IsOdd isOdd;
    cout << "test1: " << my_not1(ptr_fun(yes))(true) << endl;
    cout << "test2: " << my_not1(isOdd)(23) << endl;
    cout << "test3: " << my_not1(isOdd)(4) << endl;

    my_not_ref(isOdd)(6);
    my_not_ref(isOdd)(3);
    return a.exec();
}
